﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using AccessControl.Utility.Tools;

namespace AccessControl.Utility.UI
{
    public partial class FarsiDateRangePicker : UserControl
    {
        public DateRangeArg lastAppliedDate;
        public DateRangeArg? dateRange;
        public struct DateRangeArg
        {
            public DateTime StartDate { get; set; }
            public DateTime EndDate { get; set; }
            public DateRangeArg(DateTime startDate, DateTime endDate)
                : this()
            {
                StartDate = startDate;
                EndDate = endDate;
            }

            #region IComparable Members

            public override bool Equals(object obj)
            {
                var range = (DateRangeArg)obj;
                return (range.StartDate == StartDate && range.EndDate == EndDate);
            }

            #endregion
        }

        public FarsiDateRangePicker()
        {
            InitializeComponent();

            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = Shamsi.ThisYearStart();
            dtpEnd.SelectedDateTime = Shamsi.ThisYearEnd();
            IsSettingDateTime = false;

            SetDateText();
        }

        public event EventHandler ApplyFilter;

        private string nullError = "مجدوده تاریخ را وارد کنید";
        public string NullError
        {
            get { return nullError; }
            set { nullError = value; }
        }
        private string invalidError = "تاریخ به درستی وارد نشده است";
        public string InvalidError
        {
            get { return invalidError; }
            set { invalidError = value; }
        }
        private string errorMessage;

        protected override void SetBoundsCore(int x, int y,
           int width, int height, BoundsSpecified specified)
        {
            // Set a fixed height for the control.
            base.SetBoundsCore(x, y, width, txtDate.Height, specified);
        }

        private DateTime startDate;
        public DateTime StartDate
        {
            get
            {
                return startDate;
            }
            set
            {
                startDate = value;
                IsSettingDateTime = true;
                dtpStart.SelectedDateTime = value;
                IsSettingDateTime = false;
                SetDateText();

            }
        }

        private DateTime endDate;
        public DateTime EndDate
        {
            get
            {
                return endDate;
            }
            set
            {
                endDate = value;
                IsSettingDateTime = true;
                dtpEnd.SelectedDateTime = value;
                IsSettingDateTime = false;
                SetDateText();

            }
        }

        public void SetDate(string rangeString)
        {
            dateRange = Parse(rangeString);
            if (dateRange != null)
            {
                txtDate.Text = rangeString;
                startDate = dateRange.Value.StartDate;
                endDate = dateRange.Value.EndDate;
            }
        }

        private void txtDate_Validating(object sender, CancelEventArgs e)
        {
            dateRange = Parse(txtDate.Text);
            if (dateRange != null)
            {
                IsSettingDateTime = true;
                dtpStart.SelectedDateTime = dateRange.Value.StartDate;
                dtpEnd.SelectedDateTime = dateRange.Value.EndDate;
                IsSettingDateTime = false;
                SetDateText();
                SetDateChanged();
            }

            e.Cancel = dateRange == null;
        }

        private void txtDate_InvalidValue(object sender, DevExpress.XtraEditors.Controls.InvalidValueExceptionEventArgs e)
        {
            e.ErrorText = errorMessage;
        }

        private void txtDate_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (txtDate.IsPopupOpen) txtDate.ClosePopup();
            //if (e.KeyChar == '\r' && txtDate.DoValidate()) {
            //    SetDateChanged();
            //}
        }

        private void ucDatetimePicker_Leave(object sender, EventArgs e)
        {
            txtDate.DoValidate();
            SetDateChanged();

        }
        private void SetDateText()
        {
            if (dtpStart.SelectedDateTime.Value > dtpEnd.SelectedDateTime.Value)
            {
                IsSettingDateTime = true;
                var TempDate = dtpStart.SelectedDateTime.Value;
                dtpStart.SelectedDateTime = dtpEnd.SelectedDateTime.Value;
                dtpEnd.SelectedDateTime = TempDate;
                IsSettingDateTime = false;
            }
            txtDate.Text = dtpStart.SelectedDateTime.Value.ToShortDateString() + "-" + dtpEnd.SelectedDateTime.Value.ToShortDateString();
        }
        bool IsSettingDateTime;
        private void dtpStart_SelectedDateTimeChanged(object sender, EventArgs e)
        {
            if (dateRange.HasValue)
                dateRange = new DateRangeArg(startDate = dtpStart.SelectedDateTime.Value, dateRange.Value.EndDate);
            else
                dateRange = new DateRangeArg(startDate = dtpStart.SelectedDateTime.Value, dtpEnd.SelectedDateTime.Value);
            if (!IsSettingDateTime)
            {
                SetDateText();
                txtDate.Focus();
                txtDate.SelectionStart = 0;
                txtDate.SelectionLength = 10;
                SetDateChanged();
            }
        }

        private void dtpEnd_SelectedDateTimeChanged(object sender, EventArgs e)
        {
            if (dateRange.HasValue)
                dateRange = new DateRangeArg(dateRange.Value.StartDate, endDate = dtpEnd.SelectedDateTime.Value);
            else
                dateRange = new DateRangeArg(dtpStart.SelectedDateTime.Value, endDate = dtpEnd.SelectedDateTime.Value);
            if (!IsSettingDateTime)
            {
                SetDateText();
                txtDate.Focus();
                txtDate.SelectionStart = txtDate.Text.IndexOf("-") + 1;
                txtDate.SelectionLength = 10;
                SetDateChanged();
            }
        }

        #region Shortcuts

        private void lnkToday_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = DateTime.Now;
            dtpEnd.SelectedDateTime = DateTime.Now;
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        private void lnkThisWeek_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var pcal = new FarsiLibrary.Utils.PersianCalendar();
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = DateTime.Now.AddDays(-(int)pcal.GetDayOfWeek(DateTime.Now) - 1);
            dtpEnd.SelectedDateTime = DateTime.Now.AddDays(5 - (int)pcal.GetDayOfWeek(DateTime.Now));
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        private void lnkLastWeek_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var pcal = new FarsiLibrary.Utils.PersianCalendar();
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = DateTime.Now.AddDays(-8 - (int)pcal.GetDayOfWeek(DateTime.Now));
            dtpEnd.SelectedDateTime = DateTime.Now.AddDays(-2 - (int)pcal.GetDayOfWeek(DateTime.Now));
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        private void lnkThisMonth_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var pcal = new FarsiLibrary.Utils.PersianCalendar();
            int Year = pcal.GetYear(DateTime.Now);
            int Month = pcal.GetMonth(DateTime.Now);
            int Day = pcal.GetDayOfMonth(DateTime.Now);
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = pcal.ToDateTime(Year, Month, 1, 0, 0, 0, 0);
            dtpEnd.SelectedDateTime = pcal.ToDateTime(Year, Month, pcal.GetDaysInMonth(Year, Month), 0, 0, 0, 0);
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        private void lnkLastMonth_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var pcal = new FarsiLibrary.Utils.PersianCalendar();
            int Year = pcal.GetYear(DateTime.Now);
            int Month = pcal.GetMonth(DateTime.Now);
            int Day = pcal.GetDayOfMonth(DateTime.Now);
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = pcal.ToDateTime(Year, Month, 1, 0, 0, 0, 0);
            dtpEnd.SelectedDateTime = pcal.ToDateTime(Year, Month, pcal.GetDaysInMonth(Year, Month), 0, 0, 0, 0);
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        private void lnkThisYear_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = Shamsi.ThisYearStart();
            dtpEnd.SelectedDateTime = Shamsi.ThisYearEnd();
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        private void lnkLastYear_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = Shamsi.LastYearStart();
            dtpEnd.SelectedDateTime = Shamsi.LastYearEnd();
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }
        private void lnkNextYear_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            IsSettingDateTime = true;
            dtpStart.SelectedDateTime = Shamsi.NextYearStart();
            dtpEnd.SelectedDateTime = Shamsi.NextYearEnd();
            IsSettingDateTime = false;
            SetDateText();
            SetDateChanged();
            txtDate.SelectAll();
        }

        #endregion


        public DateRangeArg? Parse(string range)
        {

            errorMessage = null;
            if (string.IsNullOrEmpty(range))
            {
                errorMessage = NullError;
            }
            else
            {
                range = range.Trim();

                var rxDate = new Regex("(?<Year>13\\d{2})[-/\\, ](?<Month>10|11|12|0?[1-9])[-/\\, ](?<Day>(30|31|[12][0-9]|0?[1-9]))-(?<Year2>13\\d{2})[-/\\, ](?<Month2>10|11|12|0?[1-9])[-/\\, ](?<Day2>(30|31|[12][0-9]|0?[1-9]))");
                var M = rxDate.Match(range);
                if (M.Success)
                {
                    int Year = 0, Month = 0, Day = 0;
                    int Year2 = 0, Month2 = 0, Day2 = 0;
                    var pcal = new System.Globalization.PersianCalendar();
                    try
                    {
                        if (int.TryParse(M.Groups["Year"].Value, out Year) &&
                            int.TryParse(M.Groups["Month"].Value, out Month) &&
                            int.TryParse(M.Groups["Day"].Value, out Day) &&
                            int.TryParse(M.Groups["Year2"].Value, out Year2) &&
                            int.TryParse(M.Groups["Month2"].Value, out Month2) &&
                            int.TryParse(M.Groups["Day2"].Value, out Day2))
                        {

                            Day = Math.Min(Day, pcal.GetDaysInMonth(Year, Month));
                            Day2 = Math.Min(Day2, pcal.GetDaysInMonth(Year2, Month2));

                            DateTime startDate = pcal.ToDateTime(Year, Month, Day, 0, 0, 0, 0);
                            DateTime endDate = pcal.ToDateTime(Year2, Month2, Day2, 0, 0, 0, 0);
                            if (startDate < endDate)
                                return new DateRangeArg(startDate, endDate);
                            else
                                return new DateRangeArg(endDate, startDate);
                        }
                        else errorMessage = InvalidError;
                    }
                    catch (ArgumentOutOfRangeException ex)
                    {
                        if (pcal.GetDaysInMonth(Year, Month) < Day)
                            errorMessage = "روز تاریخ ابتدا باید بین 1 و " + pcal.GetDaysInMonth(Year, Month) + " باشد";
                        else if (pcal.GetDaysInMonth(Year2, Month2) < Day2)
                            errorMessage = "روز تاریخ انتها باید بین 1 و " + pcal.GetDaysInMonth(Year, Month2) + " باشد";
                        else
                            errorMessage = ex.Message;
                    }
                    catch (Exception ex)
                    {
                        errorMessage = ex.Message;
                    }

                }
                else
                {
                    errorMessage = InvalidError;
                }
            }
            return null;
        }

        private void txtDate_EditValueChanged(object sender, EventArgs e)
        {
        }

        private void txtDate_Popup(object sender, EventArgs e)
        {
            dateRange = Parse(txtDate.Text);
            if (dateRange.HasValue)
            {
                IsSettingDateTime = true;
                dtpStart.SelectedDateTime = dateRange.Value.StartDate;
                dtpEnd.SelectedDateTime = dateRange.Value.EndDate;
                IsSettingDateTime = false;
            }
            else
            {
                SetDateText();
            }
        }


        private void txtDate_Closed(object sender, DevExpress.XtraEditors.Controls.ClosedEventArgs e)
        {
            SetDateText();
            SetDateChanged();
        }

        public void SetDateChanged()
        {
            if (ApplyFilter != null && !lastAppliedDate.Equals(dateRange.Value))
            {
                lastAppliedDate = dateRange.Value;
                ApplyFilter(this, EventArgs.Empty);
            }
        }
    }
}
